/*
 * Copyright (c) 2013-2018 上海汇数数据. All rights reserved.
 * @(#) OpsOutVO.java 2018-08-08 17:43
 */

package cn.jh.common.core.ret;

import cn.jh.common.core.i18n.I18n;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import static com.google.common.base.Preconditions.checkArgument;


/**
 * 操作（动作）执行的响应数据模型。
 *
 * @author Fuchun
 * @since 1.0
 */
public abstract class OpsOutVO extends BaseOutVO implements OutVO {

    private static final long serialVersionUID = -1L;

    public static OkBuilder ok() {
        return new OkBuilder();
    }

    public static FailBuilder fail() {
        return new FailBuilder(Constants.FAILURE_CODE);
    }

    public static FailBuilder fail(@NotNull String code) {
        return new FailBuilder(code);
    }

    /**
     * 操作发生的字段（属性）错误。
     */
    @Nullable
    public abstract List<FieldError> getErrors();

    @Override
    public String toString() {
        return toStringHelper().add("errors", getErrors()).toString();
    }

    public static abstract class Builder<B extends Builder<B>> {

        final String code;
        @Nullable String message;
        @Nullable String description;
        @Nullable Serializable data;

        public Builder(@NotNull String code) {
            this.code = code;
        }

        void message(String i18nCode, @Nullable String i18nCodeWithInfo, @Nullable String what) {
            String message;
            if (what != null) {
                checkArgument(i18nCodeWithInfo != null,
                        "The i18nCodeWithInfo must not be null when `what` is NotNull");
                message = I18n.get(i18nCodeWithInfo, new Object[]{what});
            } else {
                message = I18n.get(i18nCode);
            }
            this.message = message;
        }

        @SuppressWarnings("unchecked")
        B me() {
            return (B) this;
        }

        public B description(String description) {
            this.description = description;
            return me();
        }

        public OpsOutVO data(@Nullable Serializable data) {
            this.data = data;
            return buildVO();
        }

        protected abstract OpsOutVO buildVO();

        public OpsOutVO build() {
            return data(null);
        }
    }

    public static class OkBuilder extends Builder<OkBuilder> {

        OkBuilder() {
            super(Constants.SUCCESS_CODE);
        }

        /** 操作已成功。 */
        public OkBuilder op() {
            message("successful.op", null, null);
            return this;
        }

        public OkBuilder execution(@Nullable String what) {
            message("successful.execution", "successful.execution.withInfo", what);
            return this;
        }

        public OkBuilder modified(@Nullable String what) {
            message("successful.modified", "successful.modified.withInfo", what);
            return this;
        }

        public OkBuilder created(@Nullable String what) {
            message("successful.created", "successful.created.withInfo", what);
            return this;
        }

        public OkBuilder updated(@Nullable String what) {
            message("successful.updated", "successful.updated.withInfo", what);
            return this;
        }

        public OkBuilder deleted(@Nullable String what) {
            message("successful.deleted", "successful.deleted.withInfo", what);
            return this;
        }

        public OkBuilder added(@Nullable String what) {
            message("successful.added", "successful.added.withInfo", what);
            return this;
        }

        @Override
        protected OpsOutVO buildVO() {
            return new DefaultOpsOutVO(code, message, description, data, null);
        }
    }

    public static class FailBuilder extends Builder<FailBuilder> {

        private final List<FieldError> innerErrors = new ArrayList<>();

        FailBuilder(@NotNull String code) {
            super(code);
        }

        /** 操作失败。*/
        public FailBuilder op() {
            message("failure.op", null, null);
            return this;
        }

        public FailBuilder execution(@Nullable String what) {
            message("failure.execution", "failure.execution.withInfo", what);
            return this;
        }

        public FailBuilder modify(@Nullable String what) {
            message("failure.modify", "failure.modify.withInfo", what);
            return this;
        }

        public FailBuilder create(@Nullable String what) {
            message("failure.create", "failure.create.withInfo", what);
            return this;
        }

        public FailBuilder update(@Nullable String what) {
            message("failure.update", "failure.update.withInfo", what);
            return this;
        }

        public FailBuilder delete(@Nullable String what) {
            message("failure.delete", "failure.delete.withInfo", what);
            return this;
        }

        public FailBuilder add(@Nullable String what) {
            message("failure.add", "failure.add.withInfo", what);
            return this;
        }

        public FailBuilder addError(@NotNull org.springframework.validation.FieldError fe) {
            innerErrors.add(FieldError.from(fe));
            return this;
        }

        public FailBuilder addError(@NotNull FieldError fe) {
            innerErrors.add(fe);
            return this;
        }

        public FailBuilder errors(Collection<org.springframework.validation.FieldError> errors) {
            if (errors == null || errors.isEmpty()) return this;
            for (org.springframework.validation.FieldError error : errors) {
                if (error != null) {
                    innerErrors.add(FieldError.from(error));
                }
            }
            return this;
        }

        @Override
        protected OpsOutVO buildVO() {
            return new DefaultOpsOutVO(code, message, description, data, innerErrors);
        }
    }
}
